/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#pragma once

#include "hhdVector3.h"
#include "hhdQuaternion.h"
#include "hhdMatrix4.h"
#include "hhdTypes.h"
#include "hhdVector.hpp"

namespace hhd
{
	class GameObject;

	/**
	* Transform is a commonly used data structure that is used to hold the scale, rotation, and translation
	* of a GameObject in space relative to a parent GameObject. Each GameObject has one Transform that
	* belongs to it. A large number of methods are available to get out information relevant to a Transform
	* and set values in the Transform. Transform represents a specific implementation of an Affine Transform.
	* The system is left-handed.
	*
	* Internally, scale is stored as a Vector3, rotation is stored as a Quaternion, and position is stored with
	* a Vector3.
	*
	* Conceptually speaking, a Transform is like an Interface in the Engine except it is fully implemented
	* and is not extendable.
	*
	* @see GameObject
	* @see Vector3
	* @see Quaternion
	* @see Matrix4
	*/
	class Transform
	{
	public:
		/**
		* Default Constructor; No parent, zero position, identity rotation, and identity scale
		* Also new's a Matrix4 for World.
		*/
		Transform();

		/**
		* Destructor; only responsibility is to delete world matrix.
		*/
		~Transform();

		/**
		* Constructor; Takes in a GameObject to be its parent.
		*
		* @see Transform()
		*/
		Transform(GameObject* _parent);

		/**
		* initialize method; used at initialization time to makesure this transform is set up correctly.
		*
		* @param in_game_object The parent game object we are attached to. NOTE: the pass here is
		* redundant with the constructor and should probably be removed in one of the two.
		*/
		void initialize(GameObject* in_game_object);
		

		/** Setters **/

		/**
		* setPosition method; sets the position (more correctly translation) in local space.
		*/
		void setPosition(float x, float y, float z);

		/**
		* setPosition method; sets the position (more correctly translation) in local space.
		*/
		void setPosition(const Vector3& in_position);

		/**
		* setOrientation method; using a forward vector and up vector the orientation is set in local space.
		* 
		* @param forward The forward vector(z axis) to use.
		* @param up The up vector(y axis) to use. Default is (0, 1, 0).
		*/
		void setOrientation(const Vector3& forward, const Vector3& up = Vector3::up);

		/**
		* setRotation method; sets the rotation(orientation) in local space from a Quaternion.
		*
		* @param rotation The Quaternion to copy from.
		*/
		void setRotation(const Quaternion& rotation);

		/**
		* setRotationFromEuler method; sets the rotation(orientation) in local space based on three
		* Euler Angles in the order X-> Y-> Z.
		* 
		* @param x_angle The Euler Angle about X to rotate by.
		* @param y_angle The Euler Angle about Y to rotate by.
		* @param z_angle The Euler Angle about Z to rotate by.
		*/
		void setRotationFromEuler(float x_angle, float y_angle, float z_angle);

		/**
		* setScale method; sets the scale in local space.
		*/
		void setScale(float x, float y, float z);
		void setScale(const Vector3& in_scale_triple);
		
		/** Modifiers **/

		/**
		* translate method; translates in local space.
		*/
		void translate(float x, float y, float z);

		/**
		* translate method; translates in local space.
		*/
		void translate(const Vector3&);

		/** 
		* rotateX method; rotates in local space about (1,0,0) by an Euler Angle.
		*
		* @param theta The Euler Angle to rotate by.
		*/
		void rotateX(float theta);

		/** 
		* rotateY method; rotates in local space about (0,1,0) by an Euler Angle.
		*
		* @param theta The Euler Angle to rotate by.
		*/
		void rotateY(float theta);

		/** 
		* rotateZ method; rotates in local space about (0,0,1) by an Euler Angle.
		*
		* @param theta The Euler Angle to rotate by.
		*/
		void rotateZ(float theta);

		/** 
		* rotate method; rotates in local space about the three base axis and three angles.
		* The order is X-> Y-> Z.
		*
		* @param x_theta The Euler Angle to rotate around (1,0,0) by.
		* @param y_theta The Euler Angle to rotate around (0,1,0) by.
		* @param z_theta The Euler Angle to rotate around (0,0,1) by.
		*/
		void rotate(float x_theta, float y_theta, float z_theta);

		/**
		* rescale method; rescales the local scale based on three multipliers.
		*/
		void rescale(float x, float y, float z);

		/** Getters **/

		/**
		* getWorldPosition method; finds the world position. 
		*
		* @return Returns a Vector3 instance with the position.
		*/
		Vector3 getWorldPosition() const;

		/**
		* getLocalPosition method; returns the local position stored.
		*
		* @return Returns a constant Vector3 reference to the internally stored position.
		*/
		const Vector3& getLocalPosition() const;

		/**
		* getLocalScale method; returns the local scale stored.
		*
		* @return Returns a constant Vector3 reference to the internally stored scale.
		*/
		const Vector3& getLocalScale() const;

		/**
		* getLocalRotationEuler method; calculates and returns the local rotation as Euler Angles.
		* NOTE: This is not a cheap operation and should be used sparingly.
		*/
		Vector3 getLocalRotationEuler() const;

		/**
		* getRotation method; returns the local rotation stored.
		*
		* @return Returns a constant Quaternion reference to the internally stored rotation.
		*/
		const Quaternion& getRotation() const;

		/**
		* getForward method; returns an instance of a Vector3 with the local forward (z-axis) direction.
		*/
		Vector3 getForward() const;

		/**
		* getUp method; returns an instance of a Vector3 with the local up (y-axis) direction.
		*/
		Vector3 getUp() const;

		/**
		* getRight method; returns an instance of a Vector3 with the local right (x-axis) direction.
		*/
		Vector3 getRight() const;

		/**
		* getLocalMatrix method; calculates and returns a Matrix4 representation of this affine transform in local space.
		* NOTE: This is not a cheap operation and should be used sparingly.
		*/
		Matrix4 getLocalMatrix() const;

		/**
		* getWorldMatrix method; returns, and possibly calculates, the internally stored Matrix4 of this
		* affine transform in world space. This opertion is not cheap but internally it is kept track if
		* it is needs to be calculated or if the currently stored Matrix4 is correct.
		*
		* @return Returns a constant Matrix4 reference to the internally stored world matrix.
		*/
		const Matrix4& getWorldMatrix() const;

		/**
		* getWorldRotation method; calculates and returns a rotation matrix for this transform in world space.
		*
		* @return Returns a Matrix4 instance with only world rotation information stored in it.
		*/
		Matrix4 getWorldRotation();

		friend PrintStream& operator<<(PrintStream in_stream, const Transform& t);
		/* 
		* Copy method; uses assignment operator to copy over in_other to *this. A new parent
		* for this Transform can be set which should be the owning GameObject.
		*
		* @param in_other The Transform to copy from.
		* @param in_to_be_parent The GameObject to be the parent of this Transform. Default is NULL
		*
		* @return Returns *this.
		*/
		Transform& copy(const Transform& in_other, GameObject* in_to_be_parent = NULL);
	private:
		/*
		* Copy constructor; copies scale, rotation, and translation but no parent.
		*/
		Transform(const Transform& other);

		/* 
		* Assignment operator; copies scale, rotation, and translation but no parent.
		*/
		Transform& operator=(const Transform& in_other);


		GameObject* _parent;

		//scale
		Vector3 _scale;

		//rotation
		Quaternion _rotation;

		//translation
		Vector3 _position;			

		//matrix
		Matrix4* _world_matrix;

		mutable bool _world_up_to_date;

		/**
		* invalidateWorld method; called to change our _world_up_to_date flag and all of our children's
		* to false to let each Transform know its world matrix is out of date and will need to be recalculated.
		* this MUST be called by any method that changes the values in scale, rotation, or translation.
		*/
		void invalidateWorld();

		typedef uint32_t Handle;
		enum TransformState
		{
			Unused,
			Ready,
			Dirty
		};

		static Vector<Matrix4> _world_matrices;
		
	};
}